package nl.thenittygritty.specification 
{
	import nl.thenittygritty.utils.ArrayUtils;	
	import nl.thenittygritty.specification.AbstractSpecification;
	
	/**
	 * CompositeSpecification provides addSpecification and removeSpecification 
	 * methods which respectively add or remove a given specification from its 
	 * internal store. 
	 * It implements its isSatisfiedBy method by checking if the given candidate 
	 * is satisfied by all the stored specifications. 
	 * (Basically CompositeSpecification is a shortcut for chaining a lot of 
	 * specifications together with the and method.)
	 */
	public class CompositeSpecification extends AbstractSpecification 
	{
		private var specifications:Array;
		
		/**
		 * Creates a new CompositeSpecification.
		 */
		public function CompositeSpecification() 
		{
			trace("CompositeSpecification.constructor()");
			
			specifications = new Array();
		}
		
		/**
		 * Adds the given specification to its internal store and makes sure 
		 * only one copy of the given specification exists in the store before 
		 * adding it.
		 */
		public function addSpecification(specification:ISpecification):void
		{
			if (ArrayUtils.getItemIndex(specification, specifications) != -1)
			{
				specifications.push(specification);	
			}
		}
		
		/**
		 * Removes the given specification to its internal store if the given 
		 * specification exists.
		 */
		public function removeSpecification(specification:ISpecification):void
		{
			var specificationIndex:int = ArrayUtils.getItemIndex(specification, 
														specifications);
			if (specificationIndex != -1)
			{
				specifications.splice(specificationIndex, 1);
			}
		}
		
		/**
		 * Checks if the given specification is satisfied by all of the 
		 * currently stored specifications.
		 */
		override public function isSatisfiedBy(candidate:*):Boolean
		{
			for each (var specification:ISpecification in specifications)
			{
				if (specification.isSatisfiedBy(candidate) == false)
				{
					return false;	
				}
			}
			
			return true;
		}

		/**
		 * Returns the name of the class in a String.
		 */
		public function toString():String 
		{
			return "CompositeSpecification";
		}
	}
}
